package com.ruoyi.blockchain;

import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 区块链类 - 实现区块链的核心功能
 * 负责管理区块、处理交易、维护账本完整性
 */
public class Blockchain {
    // 区块链主链
    private List<Block> chain;
    // 交易池 - 存储等待打包的交易
    private List<Transaction> pendingTransactions;
    // 节点余额缓存 - 提高查询效率
    private Map<String, Double> balances;
    // 区块奖励 - 矿工挖矿获得的奖励
    private double miningReward;
    // 当前难度 - 控制挖矿难度
    private int difficulty;

    /**
     * 构造函数 - 初始化区块链
     */
    public Blockchain() {
        this.chain = new ArrayList<>();
        this.pendingTransactions = new ArrayList<>();
        this.balances = new ConcurrentHashMap<>();
        this.miningReward = 50.0;
        this.difficulty = 4;

        // 创建创世区块
        createGenesisBlock();
    }

    /**
     * 构造函数 - 自定义难度和奖励
     * @param difficulty 挖矿难度
     * @param miningReward 挖矿奖励
     */
    public Blockchain(int difficulty, double miningReward) {
        this.chain = new ArrayList<>();
        this.pendingTransactions = new ArrayList<>();
        this.balances = new ConcurrentHashMap<>();
        this.miningReward = miningReward;
        this.difficulty = difficulty;

        // 创建创世区块
        createGenesisBlock();
    }

    /**
     * 创建创世区块
     */
    private void createGenesisBlock() {
        // 创世区块没有前一个区块，前哈希值为0
        List<Transaction> genesisTransactions = new ArrayList<>();
        // 创世区块可以包含一个特殊的创始交易（比如系统发行的初始代币）
        
        Block genesisBlock = new Block(0, genesisTransactions, "0");
        genesisBlock.mineBlock(difficulty);
        chain.add(genesisBlock);
        
        // 更新余额
        updateBalances(genesisBlock);
    }

    /**
     * 获取最新的区块
     * @return 最新区块
     */
    public Block getLatestBlock() {
        return chain.get(chain.size() - 1);
    }

    /**
     * 添加新交易到交易池
     * @param transaction 交易对象
     * @return 交易是否添加成功
     */
    public boolean addTransaction(Transaction transaction) {
        // 验证交易
        if (transaction == null || !transaction.isValid()) {
            System.out.println("无效的交易，无法添加到交易池");
            return false;
        }

        // 检查发送方余额是否足够
        if (transaction.getSender() != null) {
            String senderAddress = Transaction.getStringFromKey(transaction.getSender());
            double senderBalance = getBalanceOfAddress(senderAddress);
            
            if (senderBalance < transaction.getValue()) {
                System.out.println("发送方余额不足: " + senderBalance + " < " + transaction.getValue());
                return false;
            }
        }

        // 添加到交易池
        pendingTransactions.add(transaction);
        System.out.println("交易成功添加到交易池: " + transaction.getTransactionId());
        return true;
    }

    /**
     * 挖矿并处理待处理交易
     * @param minerAddress 矿工地址（用于接收奖励）
     * @return 挖出的新区块
     */
    public Block minePendingTransactions(PublicKey minerAddress) {
        // 创建矿工奖励交易
        Transaction rewardTx = new Transaction(null, minerAddress, miningReward);
        // 系统奖励交易不需要签名（Transaction.verifySignature中会特别处理sender为null的情况）
        pendingTransactions.add(rewardTx);

        // 创建新区块
        Block newBlock = new Block(
                chain.size(),
                new ArrayList<>(pendingTransactions),  // 复制当前交易池中的交易
                getLatestBlock().getHash()
        );

        // 挖矿
        newBlock.mineBlock(difficulty);

        // 添加到区块链
        chain.add(newBlock);
        System.out.println("新区块已挖出并添加到区块链");

        // 更新余额
        updateBalances(newBlock);

        // 清空交易池，准备下一轮挖矿
        pendingTransactions.clear();

        return newBlock;
    }

    /**
     * 更新账户余额
     * @param block 包含交易的区块
     */
    private void updateBalances(Block block) {
        for (Transaction tx : block.getTransactions()) {
            // 发送方扣除金额
            if (tx.getSender() != null) {
                String senderAddress = Transaction.getStringFromKey(tx.getSender());
                balances.put(senderAddress, balances.getOrDefault(senderAddress, 0.0) - tx.getValue());
            }
            
            // 接收方增加金额
            String recipientAddress = Transaction.getStringFromKey(tx.getRecipient());
            balances.put(recipientAddress, balances.getOrDefault(recipientAddress, 0.0) + tx.getValue());
        }
    }

    /**
     * 获取指定地址的余额
     * @param address 公钥地址
     * @return 账户余额
     */
    public double getBalanceOfAddress(String address) {
        return balances.getOrDefault(address, 0.0);
    }

    /**
     * 获取指定公钥的余额
     * @param publicKey 公钥
     * @return 账户余额
     */
    public double getBalanceOfPublicKey(PublicKey publicKey) {
        String address = Transaction.getStringFromKey(publicKey);
        return getBalanceOfAddress(address);
    }

    /**
     * 验证区块链的完整性
     * @return 如果区块链有效返回true，否则返回false
     */
    public boolean isChainValid() {
        // 遍历所有区块进行验证
        for (int i = 1; i < chain.size(); i++) {
            Block currentBlock = chain.get(i);
            Block previousBlock = chain.get(i - 1);

            // 验证当前区块的哈希值
            if (!currentBlock.getHash().equals(currentBlock.calculateHash())) {
                System.out.println("区块哈希不匹配: " + i);
                return false;
            }

            // 验证区块链接（当前区块的前哈希值应等于前一个区块的哈希值）
            if (!currentBlock.getPreviousHash().equals(previousBlock.getHash())) {
                System.out.println("区块链接断开: " + i);
                return false;
            }

            // 验证工作量证明
            if (!currentBlock.getHash().substring(0, difficulty).equals(new String(new char[difficulty]).replace('\0', '0'))) {
                System.out.println("工作量证明无效: " + i);
                return false;
            }

            // 验证区块中的所有交易
            for (Transaction tx : currentBlock.getTransactions()) {
                if (!tx.isValid()) {
                    System.out.println("区块中包含无效交易: " + i);
                    return false;
                }
            }
        }

        return true;
    }

    /**
     * 替换整个区块链（用于共识机制）
     * @param newChain 新的区块链
     * @return 是否成功替换
     */
    public boolean replaceChain(List<Block> newChain) {
        // 只接受比当前链更长的链
        if (newChain.size() <= chain.size()) {
            System.out.println("新链长度不大于当前链，拒绝替换");
            return false;
        }

        // 验证新链的有效性
        Blockchain tempChain = new Blockchain(difficulty, miningReward);
        tempChain.chain = new ArrayList<>(newChain);
        
        if (!tempChain.isChainValid()) {
            System.out.println("新链无效，拒绝替换");
            return false;
        }

        // 替换链并更新余额
        chain = new ArrayList<>(newChain);
        
        // 重新计算所有余额
        recalculateAllBalances();
        
        System.out.println("区块链已成功替换为更长的有效链");
        return true;
    }

    /**
     * 重新计算所有账户的余额
     */
    private void recalculateAllBalances() {
        balances.clear();
        for (Block block : chain) {
            updateBalances(block);
        }
    }

    /**
     * 根据区块高度获取区块
     * @param index 区块高度
     * @return 区块对象，如果不存在则返回null
     */
    public Block getBlockByIndex(int index) {
        if (index >= 0 && index < chain.size()) {
            return chain.get(index);
        }
        return null;
    }

    /**
     * 根据哈希值查找区块
     * @param hash 区块哈希
     * @return 区块对象，如果不存在则返回null
     */
    public Block getBlockByHash(String hash) {
        for (Block block : chain) {
            if (block.getHash().equals(hash)) {
                return block;
            }
        }
        return null;
    }

    /**
     * 根据交易ID查找交易
     * @param transactionId 交易ID
     * @return 交易对象，如果不存在则返回null
     */
    public Transaction getTransactionById(String transactionId) {
        // 检查交易池中是否有该交易
        for (Transaction tx : pendingTransactions) {
            if (tx.getTransactionId().equals(transactionId)) {
                return tx;
            }
        }

        // 检查区块链中是否有该交易
        for (Block block : chain) {
            for (Transaction tx : block.getTransactions()) {
                if (tx.getTransactionId().equals(transactionId)) {
                    return tx;
                }
            }
        }

        return null;
    }

    /**
     * 获取区块链中的所有区块
     * @return 区块列表
     */
    public List<Block> getChain() {
        return new ArrayList<>(chain);
    }

    /**
     * 获取当前交易池中的交易
     * @return 交易列表
     */
    public List<Transaction> getPendingTransactions() {
        return new ArrayList<>(pendingTransactions);
    }

    /**
     * 获取挖矿难度
     * @return 难度值
     */
    public int getDifficulty() {
        return difficulty;
    }

    /**
     * 设置挖矿难度
     * @param difficulty 难度值
     */
    public void setDifficulty(int difficulty) {
        this.difficulty = difficulty;
    }

    /**
     * 获取挖矿奖励
     * @return 奖励金额
     */
    public double getMiningReward() {
        return miningReward;
    }

    /**
     * 设置挖矿奖励
     * @param miningReward 奖励金额
     */
    public void setMiningReward(double miningReward) {
        this.miningReward = miningReward;
    }

    /**
     * 获取区块链长度
     * @return 区块数量
     */
    public int getChainLength() {
        return chain.size();
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("Blockchain{\n");
        sb.append("  chainLength=").append(chain.size()).append(",\n");
        sb.append("  difficulty=").append(difficulty).append(",\n");
        sb.append("  miningReward=").append(miningReward).append(",\n");
        sb.append("  pendingTransactions=").append(pendingTransactions.size()).append(",\n");
        sb.append("  isChainValid=").append(isChainValid()).append("\n");
        sb.append('}');
        return sb.toString();
    }
}